/**
 * @brief 一个简单的报撤单功能演示
 *
 * 演示功能：
 * 1. 通过配置文件创建API实例；
 * 2. 启动API，启动成功后，调用login接口登录柜台；
 * 3. 等数据加载完毕后，发送一手多头开仓报单请求；
 * 4. 等待3秒后，针对所有的报单，尝试撤单；
 *  4.1 如果报单状态不可撤，打印不可撤消息至屏幕；
 *  4.2 如果报单状态可撤销，按照柜台流水号发送撤单请求；
 *  4.3 如果撤单成功，则通过onCancelOrder()接口通知撤单成功；
 *  4.4 如果撤单失败，则通过onOrder()接口通知撤单失败；
 * 5. 等待3秒后，调用logout接口登出柜台；
 */

#include <map>
#include "ExampleTrader.h"

class Example_05_Trader : public ExampleTrader {
public:
    Example_05_Trader() = default;

    ~Example_05_Trader() override {
        // release api.
        if (mApi) {
            mApi->stop();
            delete mApi;
            mApi = nullptr;
        }
    };

    void onStart(int errorCode, bool isFirstTime) override {
        ExampleTrader::onStart(errorCode, isFirstTime);

        printf("api start ok.\n");
    }

    void onStop(int reason) override {
        ExampleTrader::onStop(reason);
        printf("api stopped, reason: %d.\n", reason);
    }

    void onServerReboot() override {
        printf("trade system reboot, clear local data.\n");
        mInstrument = nullptr;
        mOrders.clear();
    }

    void onLogin(int errorCode, int exchangeCount) override {
        ExampleTrader::onLogin(errorCode, exchangeCount);

        if (errorCode != 0)
            printf("login failed, error code: %d.\n", errorCode);
        else
            printf("login success, exchange count=%d.\n", exchangeCount);
    }

    void onLogout(int errorCode) override {
        ExampleTrader::onLogout(errorCode);

        printf("logout success.\n");
    }

    void onReadyForTrading(const XTFAccount *account) override {
        ExampleTrader::onReadyForTrading(account);

        printf("ready for trading.\n");
        mInstrument = mApi->getInstrumentByID(mInstrumentID.c_str()); // 查询可用的合约对象

        // 重要：这里需要保存日内最近一次本地报单编号，报单时逐个递增，以保证本地报单的唯一性。
        mOrderLocalId = account->lastLocalOrderID;
    }

    void onLoadFinished(const XTFAccount *account) override {
        ExampleTrader::onLoadFinished(account);

        printf("load data finished.\n");
    }

    void onOrder(int errorCode, const XTFOrder *order) override {
        printf("recv order report: action=%d, local-id=%d, sys-id=%d, status=%s, error-code=%d.\n",
               order->actionType, order->localOrderID, order->sysOrderID,
               getOrderStatus(order->orderStatus), errorCode);
        if (errorCode == 0) {
            // 只处理在队列中未成交的报单
            if (order->orderStatus == XTF_OS_Queuing) {
                mOrders[order->localOrderID] = order->sysOrderID;
            }
        }
    }

    void onCancelOrder(int errorCode, const XTFOrder *cancelOrder) override {
        printf("recv cancel order report: local-id=%d, sys-id=%d, status=%s, error-code=%d.\n",
               cancelOrder->localOrderID, cancelOrder->sysOrderID,
               getOrderStatus(cancelOrder->orderStatus), errorCode);
        if (errorCode == 0 || errorCode == 1198) {
            // 如果报单被撤后，从本地列表中删除该报单信息
            auto iter = mOrders.find(cancelOrder->localOrderID);
            if (iter != mOrders.end()) {
                mOrders.erase(iter);
            }
        }
    }

    void onTrade(const XTFTrade *trade) override {
        // 打印成交明细流水
        printf("recv trade report: trade-id=%ld, price=%.4f, volume=%d/%d, sys-order-id=%d\n",
                trade->tradeID, trade->tradePrice, trade->order->totalTradedVolume,
                trade->order->orderVolume, trade->order->sysOrderID);
    }

    void start() {
        if (mApi) {
            printf("error: trader has been started.\n");
            return;
        }

        mOrderLocalId = 0;
        mApi = makeXTFApi(mConfigPath.c_str());
        if (mApi == nullptr) {
            printf("error: create xtf api failed, please check config: %s.\n", mConfigPath.c_str());
            exit(0);
        }

        printf("api starting..., config: %s.\n", mConfigPath.c_str());
        mApi->start(this);
    }

    void stop() {
        if (!mApi) {
            printf("error: trader is not started.\n");
            return;
        }

        printf("api stopping...\n");
        mApi->stop();

        // API停止操作是异步操作，需要等待一定时间，以防API对象回调时失效。
        // 4.1.664及更高版本不存在此问题，不需要增加延时。
        usleep(100000);

        delete mApi;
        mApi = nullptr;
    }

    void login() {
        if (!mApi) return;
        printf("api logging in...\n");
        mApi->login();
    }

    void logout() {
        if (!mApi) return;
        printf("api logging out...\n");
        mApi->logout();
    }

    void insertOrder() {
        if (!mApi) {
            printf("api is not started.\n");
            return;
        }

        if (!mInstrument) {
            printf("instrument is not found: %s\n", mInstrumentID.c_str());
            return;
        }

        printf("api prepare order...\n");
        XTFInputOrder order{};
        order.localOrderID = ++mOrderLocalId; // 建议使用本地唯一的编号
        order.direction = XTF_D_Buy; // or XTF_D_Sell
        order.offsetFlag = XTF_OF_Open;
        order.orderType = XTF_ODT_Limit;
        order.price = mPrice;
        order.volume = mVolume;
        order.channelSelectionType = XTF_CS_Auto;
        order.channelID = 0;
        order.orderFlag = XTF_ODF_Normal;
        order.instrument = mInstrument;

        printf("api insert order...\n");
        mApi->insertOrder(order);
    }

    void cancelOrderBySysID() {
        if (!mApi) {
            printf("api is not started.\n");
            return;
        }

        if (mOrders.empty()) {
            printf("no orders need cancel.\n");
            return;
        }

        printf("api cancel order by system id...\n");
        for (auto &iter: mOrders) {
            printf("cancel order: local-id=%d, sys-id=%d.\n", iter.first, iter.second);
            int result = mApi->cancelOrder(XTF_OIDT_System, iter.second);
            if (result != 0) {
                printf("cancel order failed, error code: %d.\n", result);
            }
            usleep(500000); // sleep 500ms;
        }
    }

    void cancelOrderByLocalID() {
        if (!mApi) {
            printf("api is not started.\n");
            return;
        }

        if (mOrders.empty()) {
            printf("no orders need cancel.\n");
            return;
        }

        printf("api cancel order by local id...\n");
        for (auto &iter: mOrders) {
            printf("cancel order: local-id=%d, sys-id=%d.\n", iter.first, iter.second);
            int result = mApi->cancelOrder(XTF_OIDT_Local, iter.first);
            if (result != 0) {
                printf("cancel order failed, error code: %d.\n", result);
            }
            usleep(500000); // sleep 500ms;
        }
    }

private:
    const XTFInstrument *mInstrument;
    std::map<int, int> mOrders;
    int mOrderLocalId;
    int mStatus = 0;
};


void runExample(const std::string &configPath, const std::string &instrumentId, double price, int volume) {
    printf("start example 01.\n");

    Example_05_Trader trader;
    trader.setConfigPath(configPath);
    trader.setInstrumentID(instrumentId);
    trader.setPrice(price);
    trader.setVolume(volume);

    trader.start();
    while (!trader.isStarted())
        trader.wait(1, "wait for trader started");

    trader.login();
    while (!trader.isLoadFinished())
        trader.wait(1, "wait for data load finished");

    trader.insertOrder();
    trader.wait(3, "wait for order inserted");

    trader.cancelOrderBySysID();
    //trader.cancelOrderByLocalID();
    trader.wait(1, "wait for order canceled");

    trader.logout();
    while (!trader.isLoggedOut())
        trader.wait(1, "wait for trader logout");

    trader.stop();
    while (!trader.isStopped())
        trader.wait(1, "wait for trader stopped");
}

int main(int argc, const char *argv[]) {
    printf("api version: %s.\n", getXTFVersion());

    // TODO: 解析传入参数，提取相关的配置
    std::string configPath = "../config/xtf_trader_api.config";
    std::string instrumentId = "au2212";
    double price = 301.50f;
    int volume = 1;
    runExample(configPath, instrumentId, price, volume);
    return 0;
}
